<?php exit();         
  

/*
krutaya doka:  http://arduino.esp8266.com/versions/1.6.5-1160-gef26c5f/doc/reference.html

curl -F "file=@css/dropdown.css;filename=/css/dropdown.css" 192.168.4.1/update
server upload: https://github.com/jpswensen/ESP8266_WebGen/blob/master/AdvancedWebServerHuzzah_SPIFFS/AdvancedWebServerHuzzah_SPIFFS.ino

 // Функция ESP.deepSleep(microseconds, mode) // Прежим глубокого сна. Для аргумента mode WAKE_RF_DEFAULT, WAKE_RFCAL, WAKE_NO_RFCAL и WAKE_RF_DISABLED.
 // Контакт GPIO16 должен быть привязан к RST – чтобы вывести чип из режима глубокого сна.
 // Функция ESP.restart() Перезапускает процессор.
 // ADC_MODE(ADC_VCC); в самом начале

 * UPGRADE: https://github.com/julienrat/esp8266_update_from_spiffs/blob/master/update_from_spiffs.ino
 * 
 * UPDATE: https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino
 * 
 *  if (!f) {
      Serial.println("file open failed");
  }  Serial.println("====== Reading from SPIFFS file =======");
  // write 10 strings to file
  for (int i=1; i<=10; i++){
    String s=f.readStringUntil('\n');
    Serial.print(i);
    Serial.print(":");
    Serial.println(s);
  }
  stream.findUntil(char target, terminal)




  HTTP.on("/list",HTTP_GET, printDirectory);
  HTTP.on("/edit",HTTP_DELETE, handleDelete);
  HTTP.on("/edit",HTTP_PUT, handleCreate);
  HTTP.on("/edit",HTTP_POST, [](){ returnOK(); }, handleFileUpload);
  HTTP.onNotFound(handleNotFound);

*/


#include "FS.h"
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <Servo.h>

#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>

#include <Ticker.h>  //Ticker Library

const String DefaultConfig="\n"
"soft=default\n"
"autoupdate=yes\n"
"server_update=http://lleo.me/ESP8266/index.php?ip={ip}&chip={chip}&CHIP={CHIP}&soft={soft}\n"
"server_ping=http://home.lleo.me/ESP8266/index.php?ip={ip}&chip={chip}&CHIP={CHIP}&soft={soft}\n"
"AP_name=ESP8266-{chip}\n"
"AP_password="
;

const 
String DefaultINDEX="<html><body><h1>Connect your WiFi for first time SPIFFS install</h1><p><form method='GET' action='/FM'>"
     "<input type='hidden' name='a' value='WIFIconn1'>"
     "<br>Login: <input type='text' name='net'>"
     "<br>Password: <input type='text' name='pass'>"
     "<br><input type='submit' value='Go'></form></body></html>"
;


#define DEFAULTPORT 80

String WIFICONNECT="";
String LOADED_CONFIG="NO";
String LOOPFILE="";

int8_t servopins[]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
Servo servo1,servo2,servo3,servo4,servo5,servo6,servo7,servo8,servo9,servo10,servo11;

Ticker tim;

unsigned int tim_count=0;
unsigned int tim_motor_detach=0;
unsigned int tim_LoopDelay=0;
// unsigned int tim_PingCounter=0;

void tim_engine() {
  
tim_count++;

  if(
tim_LoopDelaytim_LoopDelay--;
  
// if(tim_PingCounter) tim_PingCounter--;
  
  
if(tim_motor_detach) {
    if(! --
tim_motor_detach) {
      
// Serial.println("\n --- detach all servo ---");
      
for(uint8_t i=0;i<11;i++) {   if(servopins[i]!=-1ServoDetach(String(i));    }
    }
  }
  
}

// ADC_MODE(ADC_VCC);

ESP8266WiFiMulti wifiMulti;
DNSServer DNS;
ESP8266WebServer WEBDEFAULTPORT ); // CF0("WEB_PORT",DEFAULTPORT);

/*
void WiFiEvent(WiFiEvent_t event) {
    Serial.printf("\n\t\t[WiFi-event: %d", event);
    switch(event) {
        case WIFI_EVENT_STAMODE_GOT_IP: Serial.println(" Connected IP: "+ WiFi.localIP().toString()+ " ]"); break;
        case WIFI_EVENT_STAMODE_DISCONNECTED: Serial.println(" Lost connection ]"); break;
        default: Serial.println(" ]"); break;
    }
}
*/

void setup() {
  
Serial.begin(115200);
  
Serial.println("\n############### Restart #############");
  
SPIFFS.begin();

  
tim.attach(1,tim_engine); // 1 sec

  
Serial.println(FullInfo());

  
// WiFi.onEvent(WiFiEvent);
  
WIFIinit();

  
// init.txt start
  
String start=CF("start"); if(start!="") { MOTO(start); } else { start=CF("startfile"); if(start!="") { MOTO(getfile(start)); } }

 
// Serial.println("FILE `wifi_last.txt`:"+getfile("wifi_last.txt"));
 // Serial.println("MD5 `wifi_last.txt`:"+MD5file("wifi_last.txt"));

  
load_loop("loop.txt");
  
// ================================================== попытка файла ==================================================

if(WIFICONNECT!="") {

  
String s=CF("server_ping"); if(s!="") { String otv=file_get_contents(REPER(s)); Serial.println(" ["+otv+"]"); }
  
  if(
CF0("autoupdate")) UpgradeALL();

}

// ================================================== попытка файла ==================================================

 
WEB.on("/favicon.ico",HTTP_GET,WEBNAH);
 
WEB.on("/generate_204",HTTP_GET,WEBNAH);
 
WEB.on("/fwlink",HTTP_GET,WEBNAH);
 
WEB.on("/v1/public/yql",HTTP_GET,WEBNAH);
 
WEB.on("/gslb/",HTTP_GET,WEBNAH);
 
WEB.on("/chat",HTTP_POST,WEBNAH);

 
WEB.on("/time",HTTP_GET,[](){ idip("Time: "+String(tim_count)); });

 
WEB.on("/reset",HTTP_GET,[](){ ESP.reset(); });
 
WEB.on("/restart",HTTP_GET,[](){ ESP.restart(); });
 
WEB.on("/format",HTTP_GET,[](){ SPIFFS.format(); });

 
WEB.on("/PIN",HTTP_GET,[](){ String a=RE("a"); // Serial.print("\nPIN a="+a);

if(a=="servo") {
  
String n=RE("n");
  
String mode=RE("mode");
  
String s=a+n+" "+mode;
  
  if(
mode=="attach") {
    
String d=RE("d");
    if(
ServoAttached(n)) { ServoDetach(n); s+=" [detach] "; }
    
s+" #"+d;
    if(
ServoAttach(n,d.toInt())) return idip(s+" OK");
    return 
idip(s+"ERROR");
  }

  if(
mode=="detach") {
    if( ! 
ServoAttached(n) ) return idip(s+=" [not attached]");
    
ServoDetach(n);
    if( 
ServoAttached(n) ) return idip(s+" OK");
    return 
idip(s+" ERROR");
  }

  if(
mode=="go") {
    
String x=RE("x");
    
ServoWrite(n,x.toInt());
    return 
idip(s+" TO: "+x);
  }

  return 
idip("Error mode");
}

if(
a=="pinmode") {
  
String d=RE("d");
  
byte dd=(d=="A0" A0 d.toInt() );
  
String mode=RE("mode");
  
String s=a+" #"+d+" ["+String(dd)+"] "+mode;
    if(
mode=="OUTPUT"pinMode(dd,OUTPUT);
    else if(
mode=="INPUT"pinMode(dd,INPUT); 
    else if(
mode=="INPUT_PULLUP")  pinMode(dd,INPUT_PULLUP); 
    else return 
idip(s+" ERROR: unknown mode");
  return 
idip(s+" OK");
}

if(
a=="pin") {
  
String d=RE("d");
  
String mode=RE("mode");
  
String s=a+" #"+d+" "+mode;
  
digitalWrite(d.toInt(),mode.toInt());
  return 
idip(s+" OK");
}

return 
idie("PIN error");
});




// File Manager System

WEB.on("/FM",HTTP_POST,[]() { String a=RE("a");

if(
a=="editsave") {
    
String file=RE("file"); file.replace("..","");
    
String s=RE("plain");
    if(
s=="") return idie("Error length 0");
    
savefile(file,s);
    return 
otprav("clean('editor');salert('saved',300);");
}

/*

Serial.println("\nFM POST a="+a);
    
  HTTPUpload& upload = WEB.upload();

    if(!WEB.args()) Serial.println("NO ARGS");
    else {
        for(uint8_t i=0;i<WEB.args();i++) Serial.println("------> ["+WEB.argName(i)+"]=("+WEB.arg(i)+")");
    }
    return idie("UPLOAD_FILE_END");
    

  
  Serial.println("Upload status: "+String(upload.status));


  
  if (upload.status == UPLOAD_FILE_START) { Serial.println("2");
    
    String filename = upload.filename;

    Serial.printf("UPLOAD_FILE_START: %s\n", filename.c_str());
    // if(!filename.startsWith("/")) { filename = "/" + filename; }
    
    // DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
    // fsUploadFile = SPIFFS.open(filename, "w");
    // filename = String();

return idie("UPLOAD_FILE_START");
    
  } else if (upload.status == UPLOAD_FILE_WRITE) {  Serial.println("3");
    //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
    // if (fsUploadFile) { fsUploadFile.write(upload.buf, upload.currentSize); }

      Serial.printf("UPLOAD_FILE_WRITE: %u\n", upload.currentSize);

    return idie("UPLOAD_FILE_WRITE");
    
  } else if (upload.status == UPLOAD_FILE_END) {  Serial.println("4");
    // if (fsUploadFile) { fsUploadFile.close(); }
    // DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
    Serial.printf("UPLOAD_FILE_END: %u\n", upload.totalSize);


    if(!WEB.args()) Serial.println("NO ARGS");
    else {
        for(uint8_t i=0;i<WEB.args();i++) Serial.println("------> ["+WEB.argName(i)+"]=("+WEB.arg(i)+")");
    }
    return idie("UPLOAD_FILE_END");
    
  }

      else {  Serial.println("5");
        Serial.printf("UPLOAD_ZX\n");
        return idie("UPLOAD_XZ");
      }

  */
  
      
});

WEB.on("/FM",HTTP_GET,[](){ String a=RE("a"); Serial.print("\nFM a="+a);

if(
a=="WIFIconn1") {
    
savefile("wifi_last.txt",RE("net")+"\n"+RE("pass"));
    
otprav("Reboot...");
    
ESP.restart();
}

if(
a=="WIFIconn") {
  
String net=RE("net");
  
String pass=RE("pass");
  
String s="clean('wifipass'); idie('Reconnect to "+net+"');";
  
otprav(s);
  
savefile("wifi_last.txt",net+"\n"+pass);

  if(
WIFItryConnect(net,pass)) {
    
Serial.println("========> CONNECT to `"+net+"`");
    
Serial.println("========> file `wifi_last.txt` saved: ");  Serial.printlngetfile("wifi_last.txt") );
    return;
  }

  
Serial.println("========> ERROR connect to `"+net+"`");
  
WIFItryMyConnect();
  return 
idip("Error: Net: ["+net+"] / ["+pass+"]");
}

if(
a=="WiFi") {
  
String s="";
  
int n=WiFi.scanNetworks();
  if(
== 0) return otprav("salert('No Networks found',3000);");
  
  for(
int i=0i<n; ++i) {
      
s+="\n"+String(i+1)+": "+( WiFi.encryptionType(i) == ENC_TYPE_NONE " &nbsp;&nbsp; " "<i class=e_zamok></i>" )+"&nbsp;<div class=ll onclick='WIFIconn"+( WiFi.encryptionType(i) == ENC_TYPE_NONE "" "p" )+"(this.innerHTML)'>"+WiFi.SSID(i)+"</div> ("+WiFi.RSSI(i)+")";
  }
  return 
otprav("idie(toTable('"+String(n)+" networks found',\""+njs(s)+"\"))");
}

if(
a=="Files") { // список файлов
    
Dir dir SPIFFS.openDir("");
    
String s=""; while(dir.next()) {
      
String n=h(String(dir.fileName()));
      
File f=dir.openFile("r");
      
s+=",'"+n+"':"+String(f.size(),DEC);
    }
    if(
s=="") return otprav("salert('no files',2000)");
    return 
otprav("TabFiles({"+s.substring(1)+"})");
}

if(
a=="Info") { return otprav("idie(toTable('System Info',\""+njs(FullInfo())+"\"))"); }
if(
a=="Restart") { otprav("salert('Reboot... <img src=ajaxm.gif>',10000); setTimeout('document.location.reload(true)',3000);"); return ESP.restart(); }
if(
a=="Upgrade") { return otprav("salert('Upgrade: "+String(UpgradeALL()?"OK":"ERROR")+"',2000)"); }



String file=RE("file"); file.replace("..","");

if(
a=="update"||a=="upgrade") {
    
file.replace(".",""); if(file==""file="firmware";
    
upgrade_url(CF("server_update")+file+".bin");
    return;
}

if(
a=="edit") {
  
String F=fileF.toUpperCase(); if(  F.endsWith(".JPG")||F.endsWith(".JPEG")||F.endsWith(".PNG")||F.endsWith(".GIF")||F.endsWith(".ICO")||F.endsWith(".SVG")) return idie("image","<img src='"+file+"'>");
  return 
otprav("EditFile(\""+njs(file)+"\",\""+njs(getfile(file))+"\")");
}

if(
a=="save"){ return idie("Save: "+RE("file")); }

if(
a=="del") { 
  
delfile(file);
  return 
otprav("clean(\"file_"+h(file)+"\"); salert(\"Delete: "+h(file)+"\",1000);");
}
  
if(
a=="upload") {
  
String homeServer=CF("server_update");
  
file.replace("/","");
  
String o="";
  if(
file=="all.list") { Serial.println("========> UPLOAD LIST: ["+homeServer+file+"]");
      
String s=file_get_contents(homeServer+file);
      
int i; while(i>=0) { i=s.indexOf("\n"); if(i<0) break;
        
String fu=s.substring(0,i);
        
o+="<br>"+fu+" ";
        if(
fu!="" && ! fu.endsWith(".bin") && fu!="all.list"o+=file_upload_binary_o(homeServer+(homeServer.indexOf("?")>="&file=" "")+fu,fu);
        
s=s.substring(i+1);
      }
  } else {
      
Serial.println("========> UPLOAD BINARY: ["+homeServer+file+"]");
      
o+="<br>"+file+" "+file_upload_binary_o(homeServer+(homeServer.indexOf("?")>="&file=" "")+file,file);
      if(
file=="loop.txt"load_loop(file);
  }
  return 
otprav("salert(\"Updated:<br>"+o+"\",2000);");
}


return 
idie("FM error");

});



WEB.on("/AJAX",HTTP_GET,[](){ String a=RE("a"); Serial.print("\na="+a);

if(
a=="run") {
  
String s=RE("s");
  
Serial.println(" RUN:"+s);
  
MOTO(s);
  return 
otprav("");
}

if(
a=="read") {
  
uint8_t d=RE0("d");
  
uint8_t x=digitalRead(d);
  
Serial.println("Read #`"+String(d)+"`: "+x);
  return 
otprav(String(x));
}

if(
a=="aread") {
  
uint8_t d=A0// RE0("d");
  
uint16_t x=analogRead(d);
  
Serial.println("Analog Read #`A0`: "+x);
  return 
otprav(String(x));
}

if(
a=="servo") {
  
String id=RE("id");
  
uint8_t x=RE0("x");
  
Serial.println("Servo #`"+id+"` to #`"+String(x)+"`");
  
ServoWrite(id,x);
  return 
otprav("");
}

if(
a=="MOTO") {
  
String file=RE("file");
  
Serial.println("MOTO: "+file);
  
MOTO(getfile(file));
  return 
otprav("");
}

    
//http://192.168.2.60/AJAX?a=MOTO&file=SVET.txt

/*
if(a=="text") {
  String momma = CF("startfile");
  return idie("ok: "+momma);
}
*/

idip("Error a="+a);

});

WEB.onNotFound([](){ WEBFILE(String(WEB.uri())); });

WEB.begin();
}

int Loopi=0,Loopi2=0;
void loop() {
  
  
DNS.processNextRequest();
  
WEB.handleClient();

  
// if(PINGURL!="" && tim_PingCounter==0) {   tim_PingCounter=10;  }
  
  
if(LOOPFILE!="" && tim_LoopDelay==0) {
        
int loopstep=1;
        
Loopi2=LOOPFILE.indexOf("\n",Loopi);
        if(
Loopi2<0) { Loopi2=0Loopi=0; }
        else {
          
String M=LOOPFILE.substring(Loopi,Loopi2);
          if(
M!="") {
              
Serial.println("LOOP: ["+M+"]");
              if(
ARG(M,0)=="sleep"tim_LoopDelay=ARG(M,1).toInt();
              else {
                
loopstep=DOMOTO(M); // если wait или что-то еще ожидает
                
Serial.println("LOOPSTEP == "+String(loopstep));
              }
          }
          if(
loopstepLoopi=++Loopi2;
        }
  }
}

// ====================================================================
String RE(String n) {
    if(!
WEB.args()) return "";
    for(
uint8_t i=0;i<WEB.args();i++) if(WEB.argName(i)==n) return WEB.arg(i);
    return 
"";   
}

int RE0(String n) { return RE(n).toInt(); }

void WEBFILE(String path) {
  
String o = (WEB.method() == HTTP_GET )?"GET":"POST";
  
o+=": "+path;
  if(
WEB.args()) { += "\nArguments:\n"; for(uint8_t i=0;i<WEB.args();i++) o+=" "+String(WEB.argName(i))+": "+String(WEB.arg(i))+"\n"; }
  
Serial.print(o+"\n");
  
  if(
handleFileRead(path)) return;

  if(
WIFICONNECT!="") return WEBNAH();
          
  
String l="http://"+WiFi.softAPIP().toString()+"/";
  
Serial.println("Redirect: "+l);
  
WEB.sendHeader("Location",l);
  
WEB.send(302,"text/plain","Moved Permanently");
}

void WEBNAH() {
  
WEB.sendHeader("Cache-Control","no-cache, no-store, must-revalidate");
  
WEB.sendHeader("Pragma","no-cache");
  
WEB.sendHeader("Expires","-1");
  
WEB.send(404,"text/plain","404 Not Found");
}

bool handleFileRead(String path){
        
// Serial.println("Load Web: `"+path+"`");
  
if(path.endsWith("/")) path += "index.htm";
  if(
inStopWEBList(path)) return 0;
  
  
String pathWithGz path ".gz";
  if(!
SPIFFS.exists(pathWithGz) && !SPIFFS.exists(path)) {
     
Serial.println("Load Web NOT FOUND: `"+path+"`"); 
     if(
path=="/index.htm") { Serial.println("INDEX.HTM: "+DefaultINDEX); WEB.send(200,"text/html",DefaultINDEX); return true; }
     return 
false;
  }
  if(
SPIFFS.exists(pathWithGz)) path pathWithGz;
  
File file SPIFFS.open(path"r"); if(!file) return false;
  
// ersetze server mit dem Variablennamen eures ESP8266 Webservers
  
size_t sent WEB.streamFile(file,getContentType(path));
  
file.close();
  return 
true;
}


String MD5file(String file) {
    
file.replace("/",""); file="/"+file;
    if(!
SPIFFS.exists(file)) return "";
    
File f SPIFFS.open(file,"r"); if(!f) return "";
    
MD5Builder md5;
    
md5.begin();
    
md5.addStream(f,f.size()); // md5.add("Test333");
    
md5.calculate();
    
f.close();
    return 
md5.toString();
}

String MD5(String s) {
    
MD5Builder md5;
    
md5.begin();
    
md5.add(s.c_str());
    
md5.calculate();
    return 
md5.toString();
}

unsigned long filesize(String file) {
    
file.replace("/",""); file="/"+file;
    if(!
SPIFFS.exists(file)) return 0;
    
File f=SPIFFS.open(file,"r"); if(!f) return 0;
    return (
unsigned long)f.size();
}

String getContentType(String filename){
  if(
WEB.hasArg("download")) return "application/octet-stream";
  else if(
filename.endsWith(".htm") || filename.endsWith(".html") ) return "text/html";
//  else if(filename.endsWith(".svg")) return "image/svg+xml";
  
else if(filename.endsWith(".css")) return "text/css";
  else if(
filename.endsWith(".js")) return "application/javascript";
  else if(
filename.endsWith(".png")) return "image/png";
  else if(
filename.endsWith(".gif")) return "image/gif";
  else if(
filename.endsWith(".jpg")) return "image/jpeg";
  else if(
filename.endsWith(".ico")) return "image/x-icon";
//  else if(filename.endsWith(".xml")) return "text/xml";
//  else if(filename.endsWith(".pdf")) return "application/x-pdf";
  
else if(filename.endsWith(".zip")) return "application/x-zip";
  else if(
filename.endsWith(".gz")) return "application/x-gzip";
  return 
"text/plain";
}


// String ipToString(IPAddress ip){ String s=""; for(int i=0; i<4; i++) { s += i ? ".":""; s+= String(ip[i]); } return s; }
void idip(String s) { Serial.println("\nIDIE: "+s); idie(s); }
void idie(String s) { WEB.send(200,"text/plain","helps('idie',\""+njsn(s)+"\")"); }
void idie(String hm,String s) { WEB.send(200,"text/plain","helps('"+hm+"',\""+njsn(s)+"\")"); }
void otprav(String s) { WEB.send(200,"text/plain",s); }

String njsn(String s) { s.replace("\n",""); s.replace("\r",""); s.replace("\"","\\\""); return s; }
String njs(String s) { s.replace("\n","\\n");  s.replace("\r",""); s.replace("\"","\\\""); return s; }
String h(String s) { s.replace("<","&lt;"); s.replace(">","&gt;"); return s; } 

String FullInfo() { return REPER(
      
"\nChip: {CHIP}"
      "\nSoft: {soft}"
      "\nServer Update: {server_update}"
      "\nAutoupdate: {autoupdate}"

      "\nChipID/FlashID: {chip}/{FlashChipId} {CpuFreq} MHz"
      "\nPower: {vcc} mV"

      "\nWiFi: "
+(WIFICONNECT==""?"not connected, setup mode":WIFICONNECT)+""
      "\n MAC: {macAddress}"
      "\n IP: {ip}:{WEB_PORT}"
      
      "\nServer: "
      "\n AP_name: {AP_name}"
      "\n AP_password: {AP_password}"
      "\n AP_MAC: {softAPmacAddress}"
      "\n soft_AP_IP: {softAPIP}"
      
// "\n AP_mask: {AP_mask}"
      // "\n AP_ip: {AP_ip}"

"\nSketchSize: {SketchSize} MD5: {getSketchMD5}"
"\n FreeSketchSpace: {FreeSketchSpace}"
"\n FreeHeap: {FreeHeap}"
      
      "\nFlash Size: {FlashChipSize} / {FlashChipRealSize} / {FlashChipSizeByChipId} bytes"
      "\n Flash mode: {flashmode}"
      "\n FlashChipSpeed: {FlashChipSpeed}"

"\nCoreVersion: {CoreVersion}"
"\nFullVersion: {FullVersion}"
"\nBootVersion: {BootVersion}"
"\nBootMode: {BootMode}"
"\nResetReason: {ResetReason}"
"\nResetInfo: {ResetInfo}"
"\nCycles: {cycles}"

      "\nserver_ping: {server_ping}"
      "\nstartfile: {startfile}"
      "\nloopfile: {loopfile}"

"\nFiles {Files_count}: {Files}"
"\n"
 
);
}

String REPER(String s) { String l;
 
l="{ip}";  if(s.indexOf(l)>=0s.replace(l,   WiFi.localIP().toString()  );
 
 
l="{macAddress}";  if(s.indexOf(l)>=0s.replace(l,   String(WiFi.macAddress())  );
 
l="{softAPIP}";  if(s.indexOf(l)>=0s.replace(l,   WiFi.softAPIP().toString()  );
 
l="{softAPmacAddress}"; if(s.indexOf(l)>=0s.replace(l,   String(WiFi.softAPmacAddress())  );
 
l="{FreeHeap}"; if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFreeHeap(),DEC)  );
 
l="{FreeSketchSpace}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFreeSketchSpace(),DEC)  );
 
l="{SketchSize}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getSketchSize(),DEC)  );
 
 
l="{chip}";  if(s.indexOf(l)>=0s.replace(l,   get_chip()  );
 
l="{CHIP}";  if(s.indexOf(l)>=0s.replace(l,   get_CHIP()  );
 
 
l="{FlashChipId}";  if(s.indexOf(l)>=0) { String c=String(ESP.getFlashChipId(),HEX); c.toUpperCase(); s.replace(l,c);  }
 
l="{FlashChipSize}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFlashChipSize(),DEC)  );
 
l="{FlashChipRealSize}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFlashChipRealSize(),DEC)  );
 
l="{FlashChipSpeed}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFlashChipSpeed(),DEC)  );
 
 
l="{cycles}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getCycleCount(),DEC)  );

l="{CoreVersion}";  if(s.indexOf(l)>=0s.replace(l,   ESP.getCoreVersion()  );
l="{FullVersion}";  if(s.indexOf(l)>=0s.replace(l,   ESP.getFullVersion()  );
l="{BootVersion}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getBootMode(),DEC)  );
l="{BootMode}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getCycleCount(),DEC)  );
l="{CpuFreq}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getCpuFreqMHz(),DEC)  );
l="{FlashChipSizeByChipId}";  if(s.indexOf(l)>=0s.replace(l,   String(ESP.getFlashChipSizeByChipId(),DEC)  );
l="{getSketchMD5}";  if(s.indexOf(l)>=0s.replace(l,   ESP.getSketchMD5()  );

l="{ResetReason}";  if(s.indexOf(l)>=0s.replace(l,   ESP.getResetReason()  );
l="{ResetInfo}";  if(s.indexOf(l)>=0s.replace(l,   ESP.getResetInfo()  );
 
 
l="{flashmode}";  if(s.indexOf(l)>=0) { FlashMode_t m=ESP.getFlashChipMode(); s.replace(l,   String(m==FM_QIO?"QIO" m==FM_QOUT?"QOUT" m==FM_DIO?"DIO" m==FM_DOUT "DOUT" "UNKNOWN")  ); }
 
l="{vcc}";  if(s.indexOf(l)>=0) { s.replace(lString(ESP.getVcc(),DEC) ); }

 
l="{Files}"; if(s.indexOf(l)>=0) { Dir dir=SPIFFS.openDir(""); String o=""; while(dir.next()) o+=" "+String(dir.fileName()); o.trim(); s.replace(l,o); }
 
l="{Files_count}"; if(s.indexOf(l)>=0) { int k=0Dir dir=SPIFFS.openDir(""); while(dir.next()) k++; s.replace(l,String(k)); }

int a=0,b;

while( 
) {
  
a=s.indexOf("{FILE:"); if(a<0) break;
  
b=s.indexOf("}",a+6); if(b<0) break;
  
l=s.substring(a+6,b); l.trim();
  
s.replace(s.substring(a,b+1),getfile(l));
}

a=0;
while( 
) {
  
a=s.indexOf("{",a); if(a<0) break;
  
b=s.indexOf("}",a+1); if(b<0) break;
  
l=s.substring(a+1,b);
  
String lz=CF(l);
  
s.replace("{"+l+"}",lz);
  
a+=lz.length();
 }

 return 
s;
}


bool delfile(String file) {
  
file.replace("/",""); file="/"+file;
  
uint8_t i=SPIFFS.remove(file);
  
Serial.println("Delete file: "+file+" "+(i?"OK":"ERROR"));
  return 
i;
}

String getfile(String file) {
  
file.replace("/",""); file="/"+file;
  
File f=SPIFFS.open(file,"r"); if(!f) return "";
  
String s=f.readString();
  
f.close();
  return 
s;
}

void savefile(String fileString s) {
  
file.replace("/",""); file="/"+file;
  
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino

  
File f=SPIFFS.open(file,"w"); if(!f) return;

chardata[120];
  
  if(
file.endsWith(".jpg") || file.endsWith(".png")) {
      
// void serial_send(int serial_fd, char *data, int size)
//    f.write(0,40);  // f,data,
//    f.write(0);  // f,data,
//    f.write(0);  // f,data,
//    f.write(0);  // f,data,
//    f.write(0);  // f,data,
    
Serial.println("Can't write BINARY file: "+file); return;
    
  } else 
f.print(s);
  
  
f.close();
}












bool WIFItryConnect(String ssid,String pass) {

//Serial.printf("Default hostname: %s\n", WiFi.hostname().c_str());
//WiFi.hostname("LLEO-TEST_02");
//Serial.printf("New hostname: %s\n", WiFi.hostname().c_str());

//   WiFi.mode(WIFI_STA);
//    Serial.print("\nWiFi.status: "); Serial.println(WiFi.status());

// Serial.printf("\n   Wi-Fi mode set to WIFI_STA %s\n", WiFi.mode(WIFI_STA) ? "" : "Failed!");
// Serial.printf("\n   WiFi.getMode(): %d\n", WiFi.getMode());
//    WiFi.mode(m): set mode to WIFI_AP, WIFI_STA, WIFI_AP_STA or WIFI_OFF
    // WiFi.getMode(): return current Wi-Fi mode (one out of four modes above)
// Serial.printf("\n   Connection status: %d\n", WiFi.status());

Serial.print("Try connecting to: "+ssid+" ");

/*
  if(WiFi.status() == WL_CONNECTED) { 
    Serial.print("Disconnecting WiFi"); WiFi.disconnect(false); WIFICONNECT=""; WiFi.disconnect(true); 
    delay(2000);
  }
  */

  
WiFi.begin(ssid.c_str(),pass.c_str());

  for(
uint8_t i=0;i<10;i++) {
    
Serial.print(".");
    
delay(500);
    if(
WiFi.status() == WL_CONNECTED) {
        
Serial.println("Connected \"" +ssid"\" IP: " WiFi.localIP().toString() );
        
WIFICONNECT=ssid;
        return 
1;
    }

  if(
WiFi.status() == WL_IDLE_STATUSSerial.println("\n - - - when Wi-Fi is in process of changing between statuses");
  if(
WiFi.status() == WL_NO_SSID_AVAILSerial.println("\n - - - in case configured SSID cannot be reached");
  if(
WiFi.status() == WL_CONNECT_FAILEDSerial.println("\n - - - if password is incorrect");
  
// if(WiFi.status() == WL_DISCONNECTED) Serial.println("\n - - - if module is not configured in station mode");
        
  
}

  
Serial.println(" ERROR connect to \"" +ssid"\"" );
  return 
0;
}

bool WIFItryMyConnect() {
  
String s=getfile("wifi_last.txt");
  if(
s!="") {
    
int i=s.indexOf("\n");
    
String ssid s.substring(0,i++);
    
String pass s.substring(i);
    
Serial.println("Try to connect last WiFi: "+ssid);
    if( 
WIFItryConnect(ssid,pass) ) return 1;
  }
  return 
0;
}

bool WIFIinit() {

  if(
WIFItryMyConnect()) return 1;

  if(
WIFItryConnect("LLeoNet","trololo123")) return 1// резервная сетка


  // перепробовать все известные нам сети

/*
  String s=getfile("wifi_list.txt"); Serial.println("\nLoad: "+s);
  if(s!="") {
      int i=0,i0=0;
      String wifi_net="";
      String wifi_pass="";
      while(i>=0) {
        i=s.indexOf("\n\n",i); if(i<0) break;
        i0=s.indexOf("\n",i+=2); if(i0<0) break;
        wifi_net=s.substring(i,i0);
        wifi_pass=s.substring(i0+=1,s.indexOf("\n",i0));
        Serial.println("NETWORK: ["+wifi_net+"] PASSWORD: ["+wifi_pass+"]");  
        wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
      }

  Serial.println("Connecting Wifi list...");
  if(wifiMulti.run() == WL_CONNECTED) {
       Serial.println("\nWiFi network \""+WiFi.SSID()+"\" connected IP: "+WiFi.localIP().toString() );
       return 1;
     }
  }
*/

  // а иначе - создать точку доступа свою
  
String AP_name=CF("AP_name");
  
String AP_password=CF("AP_password");
  
// String AP_mask=CF("AP_mask");
  // String AP_ip=CF("AP_ip");
  
Serial.println("Create WiFi-network '"+AP_name+"' password: '"+AP_password+"'"); // : IP:"+AP_ip+" MASK:"+AP_mask);
  
  
WiFi.mode(WIFI_AP);
  
// IPAddress ipsmask( ARG(AP_mask,0,".").toInt() , ARG(AP_mask,1,".").toInt() , ARG(AP_mask,2,".").toInt() , ARG(AP_mask,3,".").toInt() );
  // IPAddress ips( ARG(AP_ip,0,".").toInt() , ARG(AP_ip,1,".").toInt() , ARG(AP_ip,2,".").toInt() , ARG(AP_ip,3,".").toInt() );
  // WiFi.softAPConfig(ips,ips,ipsmask);

  
if(AP_password!=""WiFi.softAP(AP_name.c_str(),AP_password.c_str()); else WiFi.softAP(AP_name.c_str());
  
delay(500); // Without delay I've seen the IP address blank
  
Serial.println("Connected: "+WiFi.softAPIP().toString() );
  
DNS.setErrorReplyCode(DNSReplyCode::NoError);
  
DNS.start(53,"*",WiFi.softAPIP());

  
Serial.println("\n\n"+FullInfo());
    
  return 
0;
}


String get_CHIP() { String l=CF("NAME"); return (l!="" get_chip() ); }
String get_chip() { String l=String(ESP.getChipId(),HEX); l.toUpperCase(); return l; }


String file_upload_binary_o(String url,String file) {
  
int i=file_upload_binary(url,file);
  if(
i!=0) return "<font color=red>ERROR #"+String(i)+"</font>";
  return 
"<font color=green>OK</font>";
}

int8_t file_upload_binary(String url,String file) { if(inStopList(file)) return 0;
  
int8_t res=0;
  
HTTPClient http;
  
http.setTimeout(5000);   
  
http.begin(url.c_str());
  
// Serial.println("File_UPLOAD_BINARY_contents: "+url+" ");
  
int c http.GET();
  if(
== HTTP_CODE_OK) {

        
file.replace("/",""); file="/"+file;
        
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino
        
File f=SPIFFS.open(file,"w"); if(!fres=-3;
        else {

       
int len http.getSize();
        
// create buffer for read
       
uint8_t buff[128] = { };

        
// get tcp stream
        
WiFiClient stream http.getStreamPtr();

        
// read all data from server
        
while (http.connected() && (len || len == -1)) {
          
          
// get available data size
          
size_t size stream->available();
          
          if(
size) {
            
// read up to 128 byte
            
int c stream->readBytes(buff, ((size sizeof(buff)) ? sizeof(buff) : size));
            
// write it to f
            
f.write(buffc);
            if(
len 0len -= c;
          }
          
delay(1);
        }
    } 
f.close();
  } else { 
res=-1Serial.printf("\terror #%d: %s\n"chttp.errorToString(c).c_str()); }
  
http.end();
  return 
res;
}






String file_get_contents(String url) {
  
HTTPClient http;
  
http.setTimeout(5000);
  
http.begin(url.c_str());
  
Serial.println("File_get_contents: ["+url+"]");
  
int c http.GET();
  
String s="";
  if(
== HTTP_CODE_OKs=http.getString();
  else 
Serial.printf("\terror #%d: %s\n"chttp.errorToString(c).c_str());
  
http.end();
  return 
s;
}


bool ServoDetach(String n) {
      if(!
ServoAttached(n)) return 0;
      
uint8_t ni=servopins[n.toInt()];
      if(
n=="1") { servo1.detach();/* pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="2") { if(ni!=-1servo2.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="3") { if(ni!=-1servo3.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="4") { if(ni!=-1servo4.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="5") { if(ni!=-1servo5.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="6") { if(ni!=-1servo6.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="7") { if(ni!=-1servo7.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="8") { if(ni!=-1servo8.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="9") { if(ni!=-1servo9.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="10") { if(ni!=-1servo10.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else if(
n=="11") { if(ni!=-1servo11.detach(); /*pinMode(ni,OUTPUT); digitalWrite(ni,0);*/ }
      else 
Serial.print(" error N");
      return 
ServoAttached(n);
}

bool ServoAttach(String nuint8_t d) {
      
uint8_t i=n.toInt(); if(i<|| i>6) { Serial.println(" error N"); return -1; }
      if(
ServoAttached(n)) return 1;
      if(
n=="1"servo1.attach(d);
      else if(
n=="2"servo2.attach(d);
      else if(
n=="3"servo3.attach(d);
      else if(
n=="4"servo4.attach(d);
      else if(
n=="5"servo5.attach(d);
      else if(
n=="6"servo6.attach(d);
      else if(
n=="7"servo7.attach(d);
      else if(
n=="8"servo8.attach(d);
      else if(
n=="9"servo9.attach(d);
      else if(
n=="10"servo10.attach(d);
      else if(
n=="11"servo11.attach(d);
      
servopins[i]=d;
      
tim_motor_detach=3;
      return 
ServoAttached(n);
}

void ServoWrite(String nint x) {
      
int i=n.toInt(); if(i<|| i>6) { Serial.println(" error N"); return; }
      
tim_motor_detach=2;
      
ServoAttach(n,servopins[i]);
        
Serial.println("... ServoWrite("+n+":"+String(servopins[i])+") = "+String(x));
      if(
n=="1"servo1.write(x);
      else if(
n=="2"servo2.write(x);
      else if(
n=="3"servo3.write(x);
      else if(
n=="4"servo4.write(x);
      else if(
n=="5"servo5.write(x);
      else if(
n=="6"servo6.write(x);
      else if(
n=="7"servo7.write(x);
      else if(
n=="8"servo8.write(x);
      else if(
n=="9"servo9.write(x);
      else if(
n=="10"servo10.write(x);
      else if(
n=="11"servo11.write(x);
}

bool ServoAttached(String n) {
    
int i=-1;
    if(
n=="1"i=servo1.attached();
    else if(
n=="2"i=servo2.attached();
    else if(
n=="3"i=servo3.attached();
    else if(
n=="4"i=servo4.attached();
    else if(
n=="5"i=servo5.attached();
    else if(
n=="6"i=servo6.attached();
    else if(
n=="7"i=servo7.attached();
    else if(
n=="8"i=servo7.attached();
    else if(
n=="9"i=servo8.attached();
    else if(
n=="10"i=servo10.attached();
    else if(
n=="11"i=servo11.attached();
    else { 
Serial.print(" error N"); return 0; }
    
// Serial.println("\n ... ServoAttach("+n+") = "+i);
    
return i;
}

void MOTO(String s) { if(s=="") return; s.replace(";","\n");
  
int i; while(i>=0) {
        
i=s.indexOf("\n"); if(i<0) { if(s.length()) DOMOTO(s); break; }
        
DOMOTO(s.substring(0,i));
        
s=s.substring(i+1);
  }
}

bool DOMOTO(String s) { s=nocomments(s); s.trim(); if(s=="") return 1String cmd=ARG(s,0);

  if(
cmd=="stop") { LOOPFILE=""Serial.println("STOP LOOP"); return 1; }
  if(
cmd=="loop") { String a=ARG(s,1); if(a==""a="loop.txt"Serial.println("START LOOP: "+a); load_loop(a); return 1; }
  if(
cmd=="ping") { String url=REPER(ARG(s,1)); String otv=file_get_contents(url); Serial.println("PING: " url " = ["+otv+"]"); MOTO(otv); return 1; }
  
  if(
cmd=="attach") { ServoAttachARG(s,1), ARG(s,2).toInt() ); return 1; }
  if(
cmd=="detach") { ServoDetachARG(s,1) ); return 1; }
  if(
cmd=="go") { ServoWriteARG(s,1), ARG(s,2).toInt() ); return 1;  }

  if(
cmd=="pinmode") {
      
uint8_t d=ARG(s,1).toInt();
      
String mode=ARG(s,2);
      if(
mode=="INPUT_PULLUP"pinMode(d,INPUT_PULLUP);
      else if(
mode=="INPUT"pinMode(d,INPUT);
      else 
pinMode(d,OUTPUT);
      return 
1;
  }

  if(
cmd=="pin") { digitalWrite(ARG(s,1).toInt(),ARG(s,2).toInt()); return 1; }

  if(
cmd=="delay") { delay(ARG(s,1).toInt()); return 1; }

  if(
cmd=="run") { MOTO(getfile(ARG(s,1))); return 1; }

  if(
cmd=="wait") { // wait 2 < 10
    
uint8_t port=ARG(s,1).toInt();
    
String z=ARG(s,2);
    
uint16_t v=ARG(s,3).toInt();
    
uint16_t x;
    
bool e;
 
    if(
== "=") { x=digitalRead(port); e=(== ); }
    else if(
== "!=") { x=digitalRead(port); e=(!= ); }
    else {
      
x=analogRead(A0);
      if(
== ">"e=();
      else if(
== ">="e=(>= );
      else if(
== "<"e=();
      else if(
== "<="e=(<= );
      else { 
Serial.println("Error WAIT: "+s); return 1; }
    }

        
// Serial.println(" . . . . WAIT: port `"+String(1*port)+"` ["+String(x)+"] "+z+" ("+String(v)+") --> "+String(e) );
        
return e;
  }

  
MOTO(getfile(cmd));
  return 
1;

}


String ARG(String s,int n,String sep) { int i=0;for(int j=0;j<n;j++) { i=s.indexOf(sep,i)+1; if(i<=0) return ""; } return s.substring(i,s.indexOf(sep,i+1)); }
String ARG(String s,int n) { return ARG(s,n," "); }

/*
String d=RE("d");
    if(ServoAttached(n)) { ServoDetach(n); s+=" [detach] "; }
    s+" #"+d;
    if(ServoAttach(n,d.toInt())) return idip(s+" OK");
*/


String nocomments(String s) { int j=s.indexOf("#"); if(j>=0) { s=s.substring(0,j); s.trim(); } return s; }

String CF(String v,String D) {
  if(
LOADED_CONFIG=="NO") {
     
LOADED_CONFIG=getfile("config.txt");
     if(
LOADED_CONFIG==""LOADED_CONFIG=DefaultConfig;
     
LOADED_CONFIG.replace("\t"," "); LOADED_CONFIG.replace("\r","");
     
LOADED_CONFIG=LOADED_CONFIG+"\n";
  }
        
int i=0,i2; while(i>=0) { i2=LOADED_CONFIG.indexOf("\n",i); if(i2<0) break;
            
String c=LOADED_CONFIG.substring(i,i2); i=++i2;
            
c=nocomments(c); if(c=="") continue;
            
int r=c.indexOf("="); if(r<0) continue;
            
String n c.substring(0,r); n.trim();
            if(
n!=v) continue;
            
c=c.substring(r+1); c.trim();
            if(
c.indexOf("{")>=0c=REPER(c);
            return 
c;
      }
      return 
D;
}
int CF0(String v,int D) { String c=CF(v,""); c.toUpperCase(); if(c=="NO"||c=="FALSE"c="0"; else if(c=="YES"||c=="TRUE"c="1"; return (c==""?D:c.toInt()); }

String CF(String v) { return CF(v,""); }
int CF0(String v) { return CF0(v,0); }

void load_loop(String s) { Loopi=0Loopi2=0tim_LoopDelay=0LOOPFILE=CF("loop"); if(LOOPFILE!=""LOOPFILE.replace(";","\n"); else {
  
// String s=CF("loopfile"); if(s!="")
  
LOOPFILE=getfile(s); } if(LOOPFILE!=""LOOPFILE+="\n"; }

String StopWebList="NO";
String StopList="NO";

bool inStopWEBList(String file) { if(StopWebList=="NO"StopWebList=getfile("stopweblist.txt"); if(StopWebList=="") return 0; return inStop(StopWebList,file); }
bool inStopList(String file) { if(StopList=="NO"StopList=getfile("stoplist.txt"); if(StopList=="") return 0; return inStop(StopList,file); }
bool inStop(String s,String file) { file.replace("/",""); file="/"+file;
  
int i=0; while(1) {
     
String l=ARG(s,i++,"\n"); if(l=="") return 0;
     if(
file.startsWith(l)) { Serial.println("Deprecated file: `"+file+"`"); return 1; }
  }
  return 
0;
}


bool upgrade_url(String file) {
    if(
inStopList(file)) return 0;
    
t_httpUpdate_return ret;
    
Serial.println("Update new sketch "+file);

     
pinMode(2,OUTPUT);digitalWrite(2,0); // ВКЛЮЧИТЬ ЛАМПУ
    
    
ESPhttpUpdate.rebootOnUpdate(true);
    
ret ESPhttpUpdate.update(file);

     
digitalWrite(2,1); // ПОГАСИТЬ ЛАМПУ
    
       
switch(ret) {
          case 
HTTP_UPDATE_FAILED: {
              
String err=ESPhttpUpdate.getLastErrorString();
              if(
err.indexOf("reset")>=0) { idip("Press RESET and repeat!"); ESP.reset(); break; }
              
idip("UPDATE ERROR ["+file+"]: "+err);
              break;
          }
          case 
HTTP_UPDATE_NO_UPDATESidip("HTTP_UPDATE_NO_UPDATES"); break;
          case 
HTTP_UPDATE_OKidip("HTTP_UPDATE_OK"); break;
          default: 
idip("OTHER"); break;
      }
}

bool UpgradeALL() {
    
String s=CF("server_update"); if(s=="") return 0;
    
String otv=file_get_contents(REPER(s)); //  Serial.print(" ["+otv+"]");
    
if(ARG(otv,0,"\n")!="OK") return 0;
    
    
String firmware_md5="";
    
int i=1; while(1) {
          
String s=ARG(otv,i++,"\n"); if(s=="") break;
          
String file=ARG(s,0," ");
          
String md5=ARG(s,1," ");
          if(
file=="firmware.bin") { firmware_md5=md5; continue; }
          if(
MD5file(file)==md5) continue;
          
Serial.print("UPDATE "+file+": ");
          
int i=file_upload_binary(CF("server_update")+"&file="+file,file); if(i==&& MD5file(file)==md5Serial.println("OK"); else Serial.println("ERROR #"+String(i));
        }
     if(
firmware_md5 != "" && firmware_md5 != ESP.getSketchMD5() ) { Serial.println("firmware.bin need to UPDATE"); upgrade_url(CF("server_update")+"&file=firmware.bin"); }
     return 
1;
}